﻿#if !UNITY_IOS || HUpdate
using System;
using System.Threading.Tasks;
using System.Net.Http;
using System.IO;
using UnityEngine;
using System.Threading;
using System.Net.Security;
using UnityModule;

namespace DownloadTool
{
    class DownloadClient_HttpClient : IDownloadClient
    {


        public static int RequestTimeoutMsLimit = 10000;
        public static TimeSpan RequestTimeout = TimeSpan.FromMilliseconds(RequestTimeoutMsLimit);
        //默认时间300 000
        public static int ReadWriteTimeoutMsLimit = 100000;

        //每次读取的字节数
        public static int BlockSize = 1024 * 1024;
        byte[] buffer = new byte[BlockSize];

        private string m_url;
        private string m_savePath;

        private Action<Exception> m_completed;
        private Action<long, long> m_progress;

        public long FileTotalBytes { get; set; }
        //private HttpWebRequest m_request;
        //速度限制控制
        public bool IsFullSpeed { get; set; } = false;

        //已下载的byte长度
        public long BytesReceived { get; private set; } = 0;
        //文件总长度
        public long TotalBytesToReceive { get; private set; } = 0;

        public bool IsDispose { get; private set; } = false;
        public bool ResumeSupport { get; set; } = false;

        public void Dispose()
        {
            IsDispose = true;
        }

        //分片下载
        //分片开头  分片结尾
        long RangeBegin = 0, RangeEnd = 0;
        //断点续传 偏移量
        long breakPoint = 0;

        FileMode mode = FileMode.Create;

        //设置分片
        public void SetRange(long begin, long end)
        {
            RangeBegin = begin;
            RangeEnd = end;
        }
        int tryCount = 0;
        public void Download(string url, string savePath, Action<Exception> completed, Action<long, long> progress)
        {
            m_url = url;
            m_savePath = savePath;
            tryCount++;
            m_completed = completed;
            m_progress = progress;
            BytesReceived = 0;
            TotalBytesToReceive = 0;
            if (ResumeSupport)
            {
                if (File.Exists(savePath))
                {
                    P.D("断点续传：" + url);
                    FileInfo fi = new FileInfo(savePath);
                    breakPoint = fi.Length;
                    RangeBegin += breakPoint;
                    mode = FileMode.Open;
                }
                else
                {
                    mode = FileMode.Create;
                }
            }
            RequestAsync();
            CheckProgressAsync();
        }
        Exception exception = null;
        //开始异步下载
        private async void RequestAsync()
        {
            //Debug.LogError($"RequestAsync:{m_url} Complete Start");
            bool result = false;
            exception = null;
            result = await Request();
            //Debug.LogError($"RequestAsync: {result} ThreadId: {Thread.CurrentThread.ManagedThreadId}, \nException: {exception}");

            Dispose();

            RefreshProgress();
            //Debug.LogError($"RequestAsync:{m_url} Complete Finish");
            CallCompleted(exception);
        }


        string GetRange()
        {
            return "bytes=" + (RangeBegin > 0 ? RangeBegin.ToString() : "0") + "-" + (RangeEnd > 0 ? RangeEnd.ToString() : "");
        }



        //System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        //同步执行下载任务
        private async Task<bool> Request()
        {
            try
            {
                using (HttpClient client = new HttpClient())
                {
                    System.Net.ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(DownloadClientConfig.CheckValidationResult);
                    RequestTimeout = TimeSpan.FromMilliseconds(RequestTimeoutMsLimit * tryCount);
                    client.Timeout = RequestTimeout;
                    HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, m_url);
                    req.Headers.Add("Range", GetRange());
                    HttpResponseMessage response = await client.SendAsync(req,HttpCompletionOption.ResponseHeadersRead);                    
                    if (response.IsSuccessStatusCode)
                    {
                        using (Stream stream = await response.Content.ReadAsStreamAsync())
                        {
                            DirectoryTool.CreateDirWithFilePath(m_savePath);
                            using (FileStream fs = new FileStream(m_savePath, mode, FileAccess.Write, FileShare.Read))
                            {
                                fs.Seek(breakPoint, SeekOrigin.Begin);
                                BytesReceived = breakPoint;
                                TotalBytesToReceive = stream.Length + breakPoint;

                                int readSize = 0;//每次读取到的数据长度，当没有数据时是-1

                                DateTime begin = DateTime.Now;
                                try
                                {
                                    //下载任务未取消时继续任务
                                    while (!IsDispose)
                                    {
                                        readSize = await stream.ReadAsync(buffer, 0, buffer.Length);
                                        if (readSize <= 0)
                                        {
                                            break;
                                        }

                                        BytesReceived += readSize;
                                        fs.Write(buffer, 0, readSize);

                                        if (BytesReceived >= TotalBytesToReceive)
                                        {
                                            // P.E("下载长度错误");
                                            break;
                                        }

                                        //不限速, 继续下载
                                        if (IsFullSpeed || DownloadClientConfig.IsFullSpeed)
                                        {
                                            continue;
                                        }

                                        // //计算下载速度
                                        // double totalSeconds = DateTime.Now.Subtract(begin).TotalSeconds;
                                        // if (totalSeconds > 0)
                                        // {
                                        //     double byteper = BytesReceived / totalSeconds;//[字节总数]除以[所花时间总秒数]=每秒获取的字节数

                                        //     if (byteper > DownloadClientConfig.ThreadSpeedLimit)           //平均下载速度超过速度限制
                                        //     {
                                        //         //下面的逻辑是通过：现实速度-限速=超速部分，通过超速部分计算要让线程休眠时长。
                                        //         double exceedSize = byteper - DownloadClientConfig.ThreadSpeedLimit;
                                        //         int sleepMS = (int)(exceedSize / DownloadClientConfig.ThreadSpeedLimit);//1000除以速度得到“每KB消耗的毫秒数”，100是一个自定义值，更好的限制下载速度
                                        //         if (sleepMS < 100)
                                        //         {
                                        //             sleepMS = 100;          //下载速度达到限速要求时, 最少休眠100毫秒
                                        //         }
                                        //         else if (sleepMS > 1000)
                                        //         {
                                        //             sleepMS = 1000;         //有时下载峰值会超大，导致休眠很长，所以强制设为最多休眠一秒。
                                        //         }

                                        //         Thread.Sleep(sleepMS);      // 休眠实现限速
                                        //     }
                                        // }
                                    }
                                    fs.Flush();
                                    fs.Close();
                                }
                                catch (Exception ee)
                                {
                                    Debug.LogError("read write err:" + ee.Message);
                                    exception = ee;
                                }


                            }

                        }
                    }
                    req.Dispose();
                    client.Dispose();
                    response.Dispose();
                }
            }
            catch (Exception e)
            {
                Debug.LogError("HttpClient err:" + e.Message + "");
                // sw.Stop();
                exception = e;
            }
            GC.Collect();
            return true;
        }
        //异步检测进度
        private async void CheckProgressAsync()
        {
            while (true)
            {
                await Task.Delay(200);
                if (IsDispose)
                {
                    break;
                }
                RefreshProgress();
            }
        }

        //刷新进度
        private void RefreshProgress()
        {
            m_progress?.Invoke(BytesReceived, TotalBytesToReceive);
        }

        private void CallCompleted(Exception error)
        {
            if (error != null)
            {
                Debug.LogError($"DownloadClient DownloadDataCompleted error: {m_url}\nerror: {error}");
            }
            RefreshProgress();
            m_completed?.Invoke(error);
        }
    }
}
#endif